// Copyright 2010 Abhishek Kulkarni. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package goprop

import (
	"reflect"
	"math"
)

type interval struct {
	low  float64
	high float64
}

func isInterval(i interface{}) bool {
	_, ok := i.(*interval)
	return ok
}

func Interval(a, b interface{}) *interval {
	return &interval{
		low:  a.(float64),
		high: b.(float64),
	}
}

func squareInterval(a *interval) *interval {
	return &interval{
		low:  (a.low * a.low),
		high: (a.high * a.high),
	}
}

func sqrtInterval(a *interval) *interval {
	return &interval{
		low:  float64(math.Sqrt(float64(a.low))),
		high: float64(math.Sqrt(float64(a.high))),
	}
}

func addInterval(a, b *interval) *interval {
	return &interval{
		low:  (a.low + b.low),
		high: (a.high + b.high),
	}
}

func mulInterval(a, b *interval) *interval {
	return &interval{
		low:  (a.low * b.low),
		high: (a.high * b.high),
	}
}

func divInterval(a, b *interval) *interval {
	y := &interval{
		low:  (1.0 / b.high),
		high: (1.0 / b.low),
	}
	return mulInterval(a, y)
}

func mergeInterval(a, b *interval) *interval {
	var ret *interval
	newint := intersectInterval(a, b)
	if reflect.DeepEqual(newint, a) {
		ret = a
	} else if reflect.DeepEqual(newint, b) {
		ret = b
	} else if emptyInterval(newint) {
		Error("merge failed. (%#v != %#v)\n", a, b)
	} else {
		ret = newint
	}
	return ret
}

func emptyInterval(a *interval) bool {
	return (a.low > a.high)
}

func intersectInterval(a, b *interval) *interval {
	return &interval{
		low:  float64(math.Fmax(float64(a.low), float64(b.low))),
		high: float64(math.Fmin(float64(a.high), float64(b.high))),
	}
}
